package service

import (
	"duoke/context"
	"duoke/model"
	"duoke/utils/gconv"
	"duoke/utils/gstr"
	"duoke/utils/wechat_v3"
	"errors"
	"fmt"
	"github.com/ArtisanCloud/PowerLibs/v2/object"
	"github.com/ArtisanCloud/PowerWeChat/v2/src/payment"
	"github.com/ArtisanCloud/PowerWeChat/v2/src/payment/order/request"
	refundRequest "github.com/ArtisanCloud/PowerWeChat/v2/src/payment/refund/request"
	transferRequest "github.com/ArtisanCloud/PowerWeChat/v2/src/payment/transfer/request"
	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat"
	wechat3 "github.com/go-pay/gopay/wechat/v3"
	"github.com/gogf/gf/v2/frame/g"
	"strconv"
	"time"
)

var WechatPay = new(wechatPayService)

type wechatPayService struct{}

// V3 版本实例
func (s *wechatPayService) AppV3(c *gin.Context) (*payment.Payment, error) {
	platform, err := AccountSetting.Info(c, "miniprogram")
	if err != nil {
		return nil, err
	}
	if platform == nil {
		return nil, errors.New("小程序配置信息不存在")
	}
	if gconv.String(platform["app_id"]) == "" {
		return nil, errors.New("小程序appid不存在")
	}
	if gconv.String(platform["pay_mch_id"]) == "" {
		return nil, errors.New("微信支付商户ID不存在")
	}
	// config := yaml.GetConfig()
	return payment.NewPayment(&payment.UserConfig{
		AppID:       gconv.String(platform["app_id"]),            // 小程序、公众号或者企业微信的appid
		MchID:       gconv.String(platform["pay_mch_id"]),        // 商户号 appID
		MchApiV3Key: gconv.String(platform["pay_key_v3"]),        // 微信商户里面设置的API V3密钥
		Key:         gconv.String(platform["pay_key"]),           // 微信商户里面设置的API V2密钥
		CertPath:    gconv.String(platform["pay_cert_path"]),     // 商户后台支付的Cert证书路径
		KeyPath:     gconv.String(platform["pay_cert_key_path"]), // 商户后台支付的Key证书路径
		SerialNo:    gconv.String(platform["pay_serial_no"]),     // 商户支付证书序列号
		NotifyURL:   context.Global.Domain + "/mp/notify/wxpay",
		HttpDebug:   true,
		Log: payment.Log{
			Level: "debug",
			File:  "./log/wechat_pay.log",
		},
		Http: payment.Http{
			Timeout: 30.0,
			BaseURI: "https://api.mch.weixin.qq.com",
		},
		// 可选，不传默认走程序内存
		//Cache: kernel.NewRedisClient(&kernel.RedisOptions{
		//	Addr:     config.Redis.Host + ":" + gconv.String(config.Redis.Port),
		//	Password: config.Redis.Password,
		//	DB:       config.Redis.Db,
		//}),
	})
}

// V3 统一下单
func (s *wechatPayService) OrderV3(c *gin.Context, openid string, orderNo string, payAmount float64, desc string) (interface{}, error) {
	paymentApp, err := s.AppV3(c)
	if err != nil {
		return nil, err
	}
	options := &request.RequestJSAPIPrepay{
		Amount: &request.JSAPIAmount{
			Total:    gconv.Int(payAmount * 100),
			Currency: "CNY",
		},
		Attach:      gstr.SubStr(desc, 0, 10),
		Description: gstr.SubStr(desc, 0, 10),
		OutTradeNo:  orderNo,
		Payer: &request.JSAPIPayer{
			OpenID: openid,
		},
	}
	// 下单
	result, err := paymentApp.Order.JSAPITransaction(options)
	if err != nil {
		return nil, err
	}
	return paymentApp.JSSDK.BridgeConfig(result.PrepayID, true)
}

// V2对象
func (s *wechatPayService) AppV2(c *gin.Context) (*wechat.Client, error) {
	platform, err := AccountSetting.Info(c, "miniprogram")
	if err != nil {
		return nil, err
	}
	if platform == nil {
		return nil, errors.New("小程序配置信息不存在")
	}
	if gconv.String(platform["app_id"]) == "" {
		return nil, errors.New("小程序appid不存在")
	}
	if gconv.String(platform["pay_mch_id"]) == "" {
		return nil, errors.New("微信支付商户ID不存在")
	}
	// 初始化微信客户端
	//    appId：应用ID
	//    mchId：商户ID
	//    apiKey：API秘钥值
	//    isProd：是否是正式环境
	client := wechat.NewClient(gconv.String(platform["app_id"]), gconv.String(platform["pay_mch_id"]), gconv.String(platform["pay_key"]), true)
	// 打开Debug开关，输出请求日志，默认关闭
	client.DebugSwitch = gopay.DebugOff
	client.SetCountry(wechat.China)
	// 添加微信pem证书
	err = client.AddCertPemFilePath(gconv.String(platform["pay_cert_path"]), gconv.String(platform["pay_cert_key_path"]))
	if err != nil {
		return nil, err
	}
	return client, nil
}

// OrderV2 V2 统一下单 带分账
func (s *wechatPayService) OrderV2(c *gin.Context, notifyUrl string, openid string, orderNo string, payAmount float32, desc string, sub_mch_id string) (interface{}, error) {
	client, err := s.AppV2(c)
	if err != nil {
		return nil, err
	}
	bm := make(gopay.BodyMap)
	nonceStr := object.QuickRandom(32)
	bm.Set("nonce_str", nonceStr).
		Set("body", desc).
		Set("out_trade_no", orderNo).
		Set("total_fee", gconv.Int(payAmount*100)).
		Set("spbill_create_ip", context.Global.Ip).
		Set("notify_url", notifyUrl).
		Set("trade_type", "JSAPI").
		Set("sign_type", wechat.SignType_MD5).
		Set("openid", openid)
	if sub_mch_id != "" {
		bm.Set("profit_sharing", "Y").
			Set("sub_mch_id", sub_mch_id)
	}
	wxRsp, err := client.UnifiedOrder(c, bm)
	if err != nil {
		return nil, err
	}
	fmt.Println("支付返回：", wxRsp)
	if wxRsp.ReturnCode != "SUCCESS" {
		return nil, errors.New(wxRsp.ReturnMsg)
	}
	if wxRsp.ResultCode != "SUCCESS" {
		return nil, errors.New(wxRsp.ErrCode + wxRsp.ErrCodeDes)
	}
	timeStamp := strconv.FormatInt(time.Now().Unix(), 10)
	packages := "prepay_id=" + wxRsp.PrepayId
	paySign := wechat.GetMiniPaySign(client.AppId, nonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey)
	options := map[string]string{
		"paySign":   paySign,
		"timeStamp": timeStamp,
		"nonceStr":  nonceStr,
		"package":   packages,
		"signType":  "MD5",
	}
	return options, nil
}

func (s *wechatPayService) OrderPay(c *gin.Context, req *model.ReqWechatPay) (interface{}, error) {
	client, err := s.AppV2(c)
	if err != nil {
		return nil, err
	}
	bm := make(gopay.BodyMap)
	nonceStr := object.QuickRandom(32)
	bm.Set("nonce_str", nonceStr).
		Set("body", req.Desc).
		Set("out_trade_no", req.OrderNo).
		Set("total_fee", gconv.Int(req.PayAmount*100)).
		Set("spbill_create_ip", context.Global.Ip).
		Set("notify_url", req.NotifyUrl).
		Set("trade_type", "JSAPI").
		Set("sign_type", wechat.SignType_MD5).
		Set("openid", req.Openid)
	if req.SubMchId != "" {
		bm.Set("profit_sharing", "Y").
			Set("sub_mch_id", req.SubMchId)
	}
	wxRsp, err := client.UnifiedOrder(c, bm)
	if err != nil {
		return nil, err
	}
	fmt.Println("支付返回：", wxRsp)
	if wxRsp.ReturnCode != "SUCCESS" {
		return nil, errors.New(wxRsp.ReturnMsg)
	}
	if wxRsp.ResultCode != "SUCCESS" {
		return nil, errors.New(wxRsp.ErrCode + wxRsp.ErrCodeDes)
	}
	timeStamp := strconv.FormatInt(time.Now().Unix(), 10)
	packages := "prepay_id=" + wxRsp.PrepayId
	paySign := wechat.GetMiniPaySign(client.AppId, nonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey)
	options := map[string]string{
		"paySign":   paySign,
		"timeStamp": timeStamp,
		"nonceStr":  nonceStr,
		"package":   packages,
		"signType":  "MD5",
	}
	return options, nil
}

// V2发零钱
func (s *wechatPayService) TransferV2(c *gin.Context, openid string, amount float64, orderNo string, desc string) (bool, error) {
	client, err := s.AppV2(c)
	if err != nil {
		return false, err
	}
	result, err := client.Transfer(c, gopay.BodyMap(gin.H{
		"partner_trade_no": orderNo, // 商户订单号，需保持唯一性(只能是字母或者数字，不能包含有符号)
		"openid":           openid,
		"nonce_str":        object.QuickRandom(32),
		"spbill_create_ip": context.Global.Ip,
		"check_name":       "NO_CHECK",              // NO_CHECK：不校验真实姓名, FORCE_CHECK：强校验真实姓名
		"amount":           gconv.Int(amount * 100), // 企业付款金额，单位为分
		"desc":             desc,                    // 企业付款操作说明信息。必填
	}))
	if err != nil {
		return false, err
	}
	if result.ReturnCode != "SUCCESS" {
		return false, errors.New(result.ReturnMsg)
	}
	if result.ResultCode == "FAIL" {
		return false, errors.New(result.ErrCodeDes)
	}
	return true, nil
}

// V3转账
func (s *wechatPayService) TransferV3(c *gin.Context, openid string, amount float64, orderNo string, desc string) (bool, error) {
	paymentApp, err := s.AppV3(c)
	if err != nil {
		return false, err
	}
	transfer := &transferRequest.RequestTransferBatch{
		AppID:       "{APPID}",
		OutBatchNO:  orderNo,
		BatchName:   "batch-1",
		BatchRemark: "batch-1-remark",
		TotalAmount: gconv.Int(amount * 100),
		TotalNum:    1,
		TransferDetailList: []*transferRequest.TransferDetail{
			&transferRequest.TransferDetail{
				OutDetailNO:    orderNo + "to1",
				TransferAmount: gconv.Int(amount * 100),
				TransferRemark: desc,
				OpenID:         openid,
			},
		},
	}
	payResult, err := paymentApp.TransferBatch.Batch(transfer)
	g.Log().Debug(c, "--------------------商转零-----------------------")
	g.Log().Debug(c, payResult)
	if err != nil {
		return false, err
	}
	if payResult.OutBatchNo == "" {
		return false, errors.New(payResult.Message)
	}
	return true, nil
}

// V3退款
func (s *wechatPayService) RefundV3(c *gin.Context, totalAmount float64, orderNo string, refundAmount float64) (bool, error) {
	paymentApp, err := s.AppV3(c)
	if err != nil {
		return false, err
	}
	options := &refundRequest.RequestRefund{
		OutTradeNo:  orderNo,
		OutRefundNo: "r" + orderNo,
		Reason:      "",
		//NotifyUrl:     "", // 异步接收微信支付退款结果通知的回调地址
		Amount: &refundRequest.RefundAmount{
			Currency: "CNY",
			Refund:   gconv.Int(refundAmount * 100),       // 退款金额，单位：分
			Total:    gconv.Int(totalAmount * 100),        // 订单总金额，单位：分
			From:     []*refundRequest.RefundAmountFrom{}, // 退款出资账户及金额。不传仍然需要这个空数组防止微信报错
		},
		GoodsDetail: nil,
	}
	refund, err := paymentApp.Refund.Refund(options)
	if err != nil {
		return false, err
	}
	if refund.RefundID == "" {
		return false, errors.New(refund.Message)
	}
	return true, nil
}

// V3奖励模式退款
func (s *wechatPayService) RewardRefund(c *gin.Context, amount float64, totalAmount float64, orderNo string) (bool, error) {
	paymentApp, err := s.AppV3(c)
	if err != nil {
		return false, err
	}
	options := &refundRequest.RequestRefund{
		OutTradeNo:  orderNo,
		OutRefundNo: "r" + orderNo,
		Reason:      "",
		//NotifyUrl:     "", // 异步接收微信支付退款结果通知的回调地址
		Amount: &refundRequest.RefundAmount{
			Currency: "CNY",
			Refund:   gconv.Int(amount * 100),             // 退款金额，单位：分
			Total:    gconv.Int(totalAmount * 100),        // 订单总金额，单位：分
			From:     []*refundRequest.RefundAmountFrom{}, // 退款出资账户及金额。不传仍然需要这个空数组防止微信报错
		},
		GoodsDetail: nil,
	}
	refund, err := paymentApp.Refund.Refund(options)
	if err != nil {
		return false, err
	}
	if refund.RefundID == "" {
		return false, errors.New(refund.Message)
	}
	return true, nil
}
func (s *wechatPayService) PartnerTransfer(c *gin.Context, req *model.Transfer) (*wechat3.TransferBillsRsp, error) {
	account, err := Request.Account(c)
	if err != nil || account.Id == 0 {
		return nil, errors.New("获取账户信息错误")
	}
	set := new(accountSettingService)
	res, err := set.InfoById(account.Id, "miniprogram")
	if err != nil {
		return nil, err
	}
	rsp, err := wechat_v3.NewConfig(res).Transfer(c, req)
	if err != nil {
		return nil, err
	}
	return rsp, err
}
